import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;

public class FaceCollage {

    private static String mainImagePath = "C:/Users/Lhy/Pictures/matri.jpg";
    private static String sourceFilesPath = "C:/Users/Lhy/Pictures/Matri/Recepcion/";
    private static String CollageFilePath = "C:/Users/Lhy/Pictures/";
    private static int enlargeSize = 20;
    private static int XpixelsPerBlock = 1;
    private static int YpixelsPerBlock = 1;
    File classifyFile;
    private BufferedImage mainImage;
    private int[][][] blockArray;
    private ArrayList<RGBArchivo> listOfSourceFiles;
    private int[][] sourceFileRGBs;
    private boolean[] doesSourceContribute;
    private int[][] matchArray;

    public static void main(String[] args) {
        FaceCollage fc = new FaceCollage();
    }

    public FaceCollage() {
        System.out.println("FaceCollage");
        try {
            File sourceFile = new File(mainImagePath);
            mainImage = ImageIO.read(sourceFile);
            BlockMainImage();
            GetListOfSourcefiles();
            ClassifySourceImages();
            MatchImagesWithBlocks();
            CreateResult();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void BlockMainImage() throws IOException {
        System.out.println("BlockMainImage");
        int width = mainImage.getWidth();
        int height = mainImage.getHeight();
        int[][] rgbArray = new int[width][height];
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                rgbArray[x][y] = mainImage.getRGB(x, y);
            }
        }
        int numberOfXBlocks = width / XpixelsPerBlock;
        int numberOfYBlocks = height / YpixelsPerBlock;
        blockArray = new int[numberOfXBlocks][numberOfYBlocks][3];
        for (int i = 0; i < numberOfXBlocks; i++) {
            for (int j = 0; j < numberOfYBlocks; j++) {
                int value, sumR = 0;
                int sumG = 0;
                int sumB = 0;
                for (int x = 0; x < XpixelsPerBlock; x++) {
                    for (int y = 0; y < YpixelsPerBlock; y++) {
                        value = rgbArray[i * XpixelsPerBlock + x][j * YpixelsPerBlock + y] + 16777216;
                        sumB += (value % 256);
                        value = (value / 256);
                        sumG += (value % 256);
                        value = (value / 256);
                        sumR += (value);
                    }
                }
                int resolutionPerBlock = XpixelsPerBlock * YpixelsPerBlock;
                blockArray[i][j][0] = sumR / resolutionPerBlock;
                blockArray[i][j][1] = sumG / resolutionPerBlock;
                blockArray[i][j][2] = sumB / resolutionPerBlock;
            }
        }
    }

    public void GetListOfSourcefiles() throws IOException {
        System.out.println("GetListOfSourcefiles");
        File sourceFolder = new File(sourceFilesPath);
        File[] listOfFiles = sourceFolder.listFiles();
        listOfSourceFiles = new ArrayList<File>();
        for (int i = 0; i < listOfFiles.length; i++) {
            if (listOfFiles[i].isFile()) {
                BufferedImage imageSource = ImageIO.read(listOfFiles[i]);
                if (imageSource != null) {
                    listOfSourceFiles.add(listOfFiles[i]);
                }
            }
        }
    }

    public void ClassifySourceImages() throws IOException {
        System.out.println("ClassifySourceImages");
        classifyFile = new File(CollageFilePath + "classify.txt");
        if (classifyFile.exists()) {
            FileInputStream fis = null;
            BufferedInputStream bis = null;
            DataInputStream dis = null;
            try {
                fis = new FileInputStream(classifyFile);
                bis = new BufferedInputStream(fis);
                dis = new DataInputStream(bis);
                while (dis.available() != 0) {
                    System.out.println(dis.readUTF());
                }
                fis.close();
                bis.close();
                dis.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        sourceFileRGBs = new int[listOfSourceFiles.size()][3];
        for (int i = 0; i < listOfSourceFiles.size(); i++) {
            BufferedImage imageSource = ImageIO.read(listOfSourceFiles.get(i));
            int width = imageSource.getWidth();
            int height = imageSource.getHeight();
            int value;
            int sumR = 0;
            int sumG = 0;
            int sumB = 0;
            int resolution = width * height;
            if (resolution == 0) {
                resolution = 1;
            }
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    value = imageSource.getRGB(x, y) + 16777216;
                    sumB += (value % 256);
                    value = (value / 256);
                    sumG += (value % 256);
                    value = (value / 256);
                    sumR += (value);
                }
            }
            sourceFileRGBs[i][0] = sumR / resolution;
            sourceFileRGBs[i][1] = sumG / resolution;
            sourceFileRGBs[i][2] = sumB / resolution;
        }
    }

    public void MatchImagesWithBlocks() {
        System.out.println("MatchImagesWithBlocks");
        doesSourceContribute = new boolean[listOfSourceFiles.size()];
        matchArray = new int[blockArray.length][blockArray[0].length];
        for (int j = 0; j < blockArray.length; j++) {
            for (int k = 0; k < blockArray[0].length; k++) {
                int bestDiff = -1;
                for (int i = 0; i < sourceFileRGBs.length; i++) {
                    int diff = (int) Math.sqrt(Math.pow(blockArray[j][k][0] - sourceFileRGBs[i][0], 2) + Math.pow(blockArray[j][k][1] - sourceFileRGBs[i][1], 2) + Math.pow(blockArray[j][k][2] - sourceFileRGBs[i][2], 2));
                    if (bestDiff == -1 || (diff < bestDiff)) {
                        bestDiff = diff;
                        matchArray[j][k] = i;
                    }
                }
                doesSourceContribute[matchArray[j][k]] = true;
            }
        }
    }

    public void CreateResult() throws IOException {
        System.out.println("CreateResult");
        int[][][] shortImages = new int[doesSourceContribute.length][XpixelsPerBlock * enlargeSize][YpixelsPerBlock * enlargeSize];
        for (int n = 0; n < doesSourceContribute.length; n++) {
            if (doesSourceContribute[n]) {
                BufferedImage imageSource = ImageIO.read(listOfSourceFiles.get(n));
                int width = imageSource.getWidth();
                int height = imageSource.getHeight();
                int[][] rgbArray = new int[width][height];
                for (int x = 0; x < width; x++) {
                    for (int y = 0; y < height; y++) {
                        rgbArray[x][y] = imageSource.getRGB(x, y);
                    }
                }
                int shortImageXPixel = XpixelsPerBlock * enlargeSize;
                int shortImageYPixel = YpixelsPerBlock * enlargeSize;
                int XShrinkRatio = width / shortImageXPixel;
                int YShrinkRatio = height / shortImageYPixel;
                int resolution = XShrinkRatio * YShrinkRatio;
                for (int i = 0; i < shortImageXPixel && resolution != 0; i++) {
                    for (int j = 0; j < shortImageYPixel; j++) {
                        int value;
                        int sumR = 0;
                        int sumG = 0;
                        int sumB = 0;
                        for (int x = 0; x < XShrinkRatio; x++) {
                            for (int y = 0; y < YShrinkRatio; y++) {
                                value = rgbArray[i * XShrinkRatio + x][j * YShrinkRatio + y] + 1;
                                sumB += (value % 256);
                                value = (value / 256);
                                sumG += (value % 256);
                                value = (value / 256);
                                sumR += value;
                            }
                        }
                        sumR /= resolution;
                        sumG /= resolution;
                        sumB /= resolution;
                        shortImages[n][i][j] = sumR * 65536 + sumG * 256 + sumB - 1;
                    }
                }
            }
        }
        int width = mainImage.getWidth() * enlargeSize;
        int height = mainImage.getHeight() * enlargeSize;
        BufferedImage imageResult = new BufferedImage(width, height, mainImage.getType());
        int XpixelPerBlockInResult = XpixelsPerBlock * enlargeSize;
        int YpixelPerBlockInResult = YpixelsPerBlock * enlargeSize;
        int XBlocks = width / XpixelPerBlockInResult;
        int YBlocks = height / YpixelPerBlockInResult;
        for (int i = 0; i < XBlocks; i++) {
            for (int j = 0; j < YBlocks; j++) {
                int match = matchArray[i][j];
                for (int x = 0; x < XpixelPerBlockInResult; x++) {
                    for (int y = 0; y < YpixelPerBlockInResult; y++) {
                        imageResult.setRGB((i * XpixelPerBlockInResult + x), (j * YpixelPerBlockInResult + y), shortImages[match][x][y]);
                    }
                }
            }
        }
        File resultFile = new File(CollageFilePath + "Collage" + System.currentTimeMillis() + ".jpg");
        ImageIO.write(imageResult, "jpg", resultFile);
    }
}
